import { FC, useEffect, useState, useRef, createContext } from "react"
import { v4 as uuid } from 'uuid';

export interface LoadedComponentProps {
    onLoad?: () => void;
    data?: any;
    originData?: any;
    effectKey?: string;
    loading?: boolean;
    [key: string]: any;
}

interface LoadedComponentType {
    component: FC<any>;
    componentProps?: Record<string, any>;
    index?: number;
    effectKey?: string;
    effectFn?: (originData: any, data: any) => boolean;
    skipFn?: (originData: any, data: any) => boolean;
}

interface LoaderContextType {
    effectData?: any
}

export const LoaderContext = createContext<LoaderContextType>({})

const useSequentialLoader = (components: LoadedComponentType[], effectData?: any, effect?: {
    onLoad?: (currentIndex: number) => void;
    onEnd?: () => void;
}, options?: {
    loading?: boolean;
    autoRender?: boolean;
}, componentProps?: any) => {
    const componentId = useRef<string>('');
    const loadIndexRef = useRef<number>(0);
    const loadingRef = useRef<number | undefined>(undefined);
    const endRef = useRef<boolean>(false);
    const renderRef = useRef<number[]>([]);
    const optionsLoad = useRef<boolean>(true);
    const [loadedComponents, setLoadedComponents] = useState<any[]>([]);
    const [currentLoadIndex, setCurrentLoadIndex] = useState<number>(0);
    const onLoad = () => {
        // console.log('流式加载 onLoad:_______', componentId.current, loadIndexRef.current)
        if (loadingRef.current !== undefined) renderRef.current.push(loadingRef.current)
        loadingRef.current = undefined
        loadIndexRef.current = loadIndexRef.current + 1
        setCurrentLoadIndex(loadIndexRef.current)
        if (effect?.onLoad) effect.onLoad(loadIndexRef.current)
    };
    useEffect(() => {
        componentId.current = uuid()
        optionsLoad.current = options && options.loading === true ? true : false
    }, [])
    useEffect(() => {
        components.forEach((item, index) => {
            item.index = index
        })
        if (optionsLoad.current) {
            let currentIndex: number = loadIndexRef.current
            if (options?.autoRender) {
                const renderIndexArr: number[] = []
                components.forEach((item) => {
                    if (effectData && item.effectKey && item.index !== undefined && !renderRef.current.includes(item.index)) {
                        const data = effectData[item.effectKey as string]
                        let canRender: boolean = false
                        if (item.effectFn) {
                            canRender = item.effectFn(effectData, data)
                        }
                        if (item.effectKey && !item.effectFn) {
                            canRender = effectData[item.effectKey] !== undefined
                        }
                        if (canRender) {
                            renderIndexArr.push(item.index)
                        }
                    }
                })
                if (renderIndexArr.length > 0) {
                    renderIndexArr.sort((a, b) => a - b)
                    currentIndex = renderIndexArr[0]
                }
            } else {
                currentIndex = loadIndexRef.current
            }
            // console.log('数据响应变化 onEffect:_______', componentId.current, renderRef.current, currentIndex, effectData)
            if (renderRef.current.length < components.length) {
                const renderComp = components[currentIndex]
                if (renderComp) {
                    const Component = components[currentIndex].component;
                    if (effectData && renderComp.effectKey) {
                        const data = effectData[renderComp.effectKey as string]
                        let skip: boolean = false
                        if (renderComp.skipFn) {
                            skip = renderComp.skipFn(effectData, data)
                        }
                        if (skip) {
                            loadingRef.current = currentIndex
                            setLoadedComponents((prevComponents) => [...prevComponents, null]);
                            onLoad()
                            return
                        }
                        let canRender: boolean = false
                        if (renderComp.effectFn) {
                            canRender = renderComp.effectFn(effectData, data)
                        }
                        if (renderComp.effectKey && !renderComp.effectFn) {
                            canRender = effectData[renderComp.effectKey] !== undefined
                        }
                        if (canRender && loadingRef.current === undefined && renderComp.index !== undefined && currentIndex === renderComp.index && !renderRef.current.includes(currentIndex)) {
                            loadingRef.current = currentIndex
                            const componentWithProps = <Component key={`${componentId.current}_${currentIndex}`} effectKey={renderComp.effectKey} data={data} loading={optionsLoad.current} autoRender={options?.autoRender} originData={effectData} onLoad={onLoad} {...(renderComp.componentProps || {})} {...(componentProps || {})} />;
                            setLoadedComponents((prevComponents) => [...prevComponents, componentWithProps]);
                        }
                    } else if (!renderComp.effectKey) {
                        if (loadingRef.current === undefined && renderComp.index !== undefined && currentIndex === renderComp.index && !renderRef.current.includes(currentIndex)) {
                            loadingRef.current = currentIndex
                            const componentWithProps = <Component key={`${componentId.current}_${currentIndex}`} onLoad={onLoad} loading={optionsLoad.current} autoRender={options?.autoRender} {...(renderComp.componentProps || {})}  {...(componentProps || {})} />;
                            setLoadedComponents((prevComponents) => [...prevComponents, componentWithProps]);
                        }
                    }
                }
            } else if (loadingRef.current === undefined && renderRef.current.length >= components.length && !endRef.current) {
                endRef.current = true
                if (effect?.onEnd) effect.onEnd()
            }
        } else {
            const loadArr: any[] = []
            components.forEach((item, index) => {
                const Component = item.component;
                if (effectData && item.effectKey) {
                    const data = effectData[item.effectKey as string]
                    let canRender: boolean = false
                    if (item.effectFn) {
                        canRender = item.effectFn(effectData, data)
                    }
                    if (item.effectKey && !item.effectFn) {
                        canRender = effectData[item.effectKey] !== undefined
                    }
                    if (canRender) {
                        const componentWithProps = <Component key={`${componentId.current}_${index}`} effectKey={item.effectKey} data={data} originData={effectData} {...(item.componentProps || {})}  {...(componentProps || {})} />;
                        loadArr.push(componentWithProps)
                    }
                } else if (!item.effectKey) {
                    const componentWithProps = <Component key={`${componentId.current}_${index}`}  {...(item.componentProps || {})}  {...(componentProps || {})} />;
                    loadArr.push(componentWithProps)
                }
            })
            setLoadedComponents(loadArr);
        }
    }, [effectData, currentLoadIndex]);

    return <LoaderContext.Provider value={{
        effectData
    }}>
        {loadedComponents}
    </LoaderContext.Provider>;
};

export default useSequentialLoader